﻿using System;
using System.Collections;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Practices.ServiceLocation;
using Subfaculty.Common.Entities;
using Subfaculty.Common.Interfaces.Services;

namespace Subfaculty.Infrastructure.Services
{
    public class GenerateStudyLoadForDepartment : IGenerateStudyLoadForDepartment
    {
        private IAllocationRepository _repositoryAllocation;

        public GenerateStudyLoadForDepartment()
        {
            // _repositoryAllocation = ServiceLocator.Current.GetInstance<IAllocationRepository>();
        }

        public StudyLoad GenerateStudyLoad(Department department, StudyYear studyYear)
        {
            var studyLoad = new StudyLoad();
            var studyLoadItems = new List<StudyLoadItem>();
            studyLoad.LoadItems = studyLoadItems;

            studyLoad.Department = department;
            studyLoad.StudyYear = studyYear;


            var allocations = GetAllocationsWithStudyYear(studyYear);

            foreach (var allocation in allocations)
            {
                if (allocation.StudyArea.FormStudy == ETypeFormStudy.FullTimeLearning)
                {
                    studyLoadItems.AddRange(GenerateLoadItemsForFullTimeLearning(allocation, studyLoad, department));
                }
                else if (allocation.StudyArea.FormStudy == ETypeFormStudy.EveningLearning)
                {
                    studyLoadItems.AddRange(GenerateLoadItemsForEveningLearning(allocation, studyLoad, department));
                }
                else if (allocation.StudyArea.FormStudy == ETypeFormStudy.DistanseLearning)
                {
                    studyLoadItems.AddRange(GenerateLoadItemsForDistanseLearning(allocation, studyLoad, department));
                }
            }

            return studyLoad;
        }

        //Генерация для направления с дневной формой обучения
        private IEnumerable<StudyLoadItem> GenerateLoadItemsForFullTimeLearning(Allocation allocation, StudyLoad studyLoad, Department department)
        {
            var loadItems = new List<StudyLoadItem>();

            foreach (var allocItem in allocation.AllocationItems)
            {
                if (allocItem.Department.Id != department.Id)
                    continue;

                loadItems.Add(LectureStudyRule(allocItem, allocation, studyLoad));
                loadItems.Add(PracticeStudy(allocItem, allocation, studyLoad));
                loadItems.Add(LabworkStudyRule(allocItem, allocation, studyLoad));

                loadItems.Add(ConsultationFullTimeStudyRule(allocItem, studyLoad));
                loadItems.Add(ConsultationForExamRule(allocItem, allocation, studyLoad));

                loadItems.Add(TestRule(allocItem, allocation, studyLoad));
                loadItems.Add(ExamRule(allocItem, allocation, studyLoad));
                loadItems.Add(ExamGOSRule(allocItem, allocation, studyLoad));
                loadItems.Add(SRSForFullTimeLearningRule(allocItem, allocation, studyLoad));
                loadItems.Add(RaitingRule(allocItem, allocation, studyLoad));
            }

            return loadItems;
        }

        //Генерация для направления с вечерней формой обучения
        private IEnumerable<StudyLoadItem> GenerateLoadItemsForEveningLearning(Allocation allocation, StudyLoad studyLoad, Department department)
        {
            var loadItems = new List<StudyLoadItem>();

            foreach (var allocItem in allocation.AllocationItems)
            {
                if (allocItem.Department.Id != department.Id)
                    continue;

                loadItems.Add(LectureStudyRule(allocItem, allocation, studyLoad));
                loadItems.Add(PracticeStudy(allocItem, allocation, studyLoad));
                loadItems.Add(LabworkStudyRule(allocItem, allocation, studyLoad));

                loadItems.Add(ConsultationEveningLearningRule(allocItem, studyLoad));
                loadItems.Add(ConsultationForExamRule(allocItem, allocation, studyLoad));

                loadItems.Add(TestRule(allocItem, allocation, studyLoad));
                loadItems.Add(ExamRule(allocItem, allocation, studyLoad));
                loadItems.Add(ExamGOSRule(allocItem, allocation, studyLoad));
                loadItems.Add(RaitingRule(allocItem, allocation, studyLoad));
                loadItems.Add(ControlWorkForEveningLearningRule(allocItem, allocation, studyLoad));
            }

            return loadItems;
        }

        //Генерация для направления с заочной формой обучения
        private IEnumerable<StudyLoadItem> GenerateLoadItemsForDistanseLearning(Allocation allocation, StudyLoad studyLoad, Department department)
        {
            var loadItems = new List<StudyLoadItem>();

            foreach (var allocItem in allocation.AllocationItems)
            {
                if (allocItem.Department.Id != department.Id)
                    continue;

                loadItems.Add(LectureStudyRule(allocItem, allocation, studyLoad));
                loadItems.Add(PracticeStudy(allocItem, allocation, studyLoad));
                loadItems.Add(LabworkStudyRule(allocItem, allocation, studyLoad));

                loadItems.Add(ConsultationDistanceLearnibgRule(allocItem, allocation, studyLoad));
                loadItems.Add(ConsultationForExamRule(allocItem, allocation, studyLoad));

                loadItems.Add(TestRule(allocItem, allocation, studyLoad));
                loadItems.Add(ExamRule(allocItem, allocation, studyLoad));
                loadItems.Add(ExamGOSRule(allocItem, allocation, studyLoad));
                loadItems.Add(RaitingRule(allocItem, allocation, studyLoad));
                loadItems.Add(ControlWorkForDistanceLearningRule(allocItem, allocation, studyLoad));
                loadItems.Add(CourseWorkForDistanceLearningRule(allocItem, allocation, studyLoad));
                loadItems.Add(CourseProjectForDistanceLearning(allocItem, allocation, studyLoad));

                loadItems.Add(BachelorWorkRule(allocItem, allocation, studyLoad));
            }

            return loadItems;
        }


        private AllocationItem getitem(StudyYear studyYear, int s, int d, string a)
        {
            AllocationItem item = new AllocationItem();
            item.Department = new Department() { Abbreviation = "dep", Id = 0, Title = "department" };
            item.Discipline = new Discipline() { Id = d, Abbreviation = a, Title = a };
            item.EStudyControl = EStudyControl.Exam;
            item.ETypeSRS = ETypeSRS.Project;
            item.HoursLab = 23;
            item.HoursLecture = 24;
            item.HoursPractice = 25;
            item.HoursSRS = 4;
            item.Semester = s;
            item.StudyGroups = new List<StudyGroup>();
            StudyGroup sg = new StudyGroup()
            {
                Id = 0,
                StartStudyYear = studyYear,
                StudyGroupStates = new List<StudyGroupState>(){
                    new StudyGroupState(){
                        CountStudent = 20, 
                        CountSubgroup = 30, 
                        GroupState = EGroupState.Active, 
                        Name = "ИВТ-123", 
                        Semester = s
                    }
                }
            };
            sg.StudyGroupStates.ElementAt(0).Group = sg;
            item.StudyGroups.Add(sg);
            return item;
        }
        //Получение набора раскладок с данным учебным годом годом
        private IEnumerable<Allocation> GetAllocationsWithStudyYear(StudyYear studyYear)
        {
            List<Allocation> allocations = new List<Allocation>();

            Allocation alloc = new Allocation() { Id = 0, Year = studyYear, StudyArea = new StudyArea() { FormStudy = ETypeFormStudy.EveningLearning } };
            alloc.AllocationItems = new List<AllocationItem>() { getitem(studyYear, 1, 6, "dis6"), getitem(studyYear, 2, 1, "dis1"), getitem(studyYear, 1, 2, "dis2"), getitem(studyYear, 1, 0, "dis") };

            Allocation alloc1 = new Allocation() { Id = 1, Year = studyYear, StudyArea = new StudyArea() { FormStudy = ETypeFormStudy.FullTimeLearning } };
            alloc1.AllocationItems = new List<AllocationItem>() { getitem(studyYear, 1, 5, "dis5"), getitem(studyYear, 2, 1, "dis1"), getitem(studyYear, 1, 2, "dis2"), getitem(studyYear, 1, 0, "dis") };

            Allocation alloc2 = new Allocation() { Id = 2, Year = studyYear, StudyArea = new StudyArea() { FormStudy = ETypeFormStudy.DistanseLearning } };
            alloc2.AllocationItems = new List<AllocationItem>() { getitem(studyYear, 1, 4, "dis4"), getitem(studyYear, 2, 1, "dis1"), getitem(studyYear, 1, 2, "dis2"), getitem(studyYear, 1, 0, "dis") };


            allocations.Add(alloc);
            allocations.Add(alloc1);
            allocations.Add(alloc2);
            return allocations;
        }

        #region набор правил
        #region статические выборки
        private bool IsCurrentYearGroup(StudyGroupState state, StudyGroup group, AllocationItem allocItem, Allocation allocation)
        {
            return state.Semester == (allocation.Year.StartDate - group.StartStudyYear.StartDate) * 2 + allocItem.Semester
                                     && state.GroupState == EGroupState.Active;
        }

        private int CountStudentsInSemesterRange(AllocationItem allocItem, Allocation allocation, int fromSemster, int toSemester)
        {
            return (from studyGroup in allocItem.StudyGroups
                    select
                        (from state in studyGroup.StudyGroupStates
                         where IsCurrentYearGroup(state, studyGroup, allocItem, allocation)
                                && state.Semester >= fromSemster
                                && state.Semester <= toSemester
                         select state.CountSubgroup).Sum())
            .Sum();
        }

        private int CountSudGrop(AllocationItem allocItem, Allocation allocation)
        {
            return (from studyGroup in allocItem.StudyGroups
                    select
                        (from state in studyGroup.StudyGroupStates
                         where IsCurrentYearGroup(state, studyGroup, allocItem, allocation)
                         select state.CountSubgroup).Sum())
                        .Sum();
        }

        private int CountGroup(AllocationItem allocItem, Allocation allocation)
        {
            return (from studyGroup in allocItem.StudyGroups
                    select
                         (from state in studyGroup.StudyGroupStates
                          where IsCurrentYearGroup(state, studyGroup, allocItem, allocation)
                          select state).Count())
                         .Sum();
        }

        private int CountStudents(AllocationItem allocItem, Allocation allocation)
        {
            return (from studyGroup in allocItem.StudyGroups
                    select
                        (from state in studyGroup.StudyGroupStates
                         where IsCurrentYearGroup(state, studyGroup, allocItem, allocation)
                         select state.CountStudent).Sum())
                         .Sum();
        }
        #endregion статические выборки

        #region инициализация элемента нераспределенной нагрузки
        private StudyLoadItem InitItem(AllocationItem allocItem, StudyLoad studyLoad, ETypeLoadItem typeLoadItem)
        {
            StudyLoadItem slItem = new StudyLoadItem();
            slItem.AllocationItem = allocItem;
            slItem.Discipline = allocItem.Discipline;
            slItem.StudyGroups = allocItem.StudyGroups;
            slItem.TypeLoadItem = typeLoadItem;
            slItem.StudyLoad = studyLoad;

            return slItem;
        }
        #endregion инициализация элемента нераспределенной нагрузки

        #region аудиторные занятия
        private StudyLoadItem LectureStudyRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.LectureStudy);
            slItem.Hours = allocItem.HoursLecture * CountSudGrop(allocItem, allocation);
            return slItem;
        }

        private StudyLoadItem PracticeStudy(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.PracticeStudy);
            slItem.Hours = CountSudGrop(allocItem, allocation) * allocItem.HoursPractice;
            return slItem;
        }

        private StudyLoadItem LabworkStudyRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.LabworkStudy);
            slItem.Hours = CountSudGrop(allocItem, allocation) * allocItem.HoursLab;
            return slItem;
        }
        #endregion region аудиторные занятия

        #region консультации
        private StudyLoadItem ConsultationFullTimeStudyRule(AllocationItem allocItem, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.ConsultationForFullTimeLearning);
            slItem.Hours = (double)allocItem.HoursLecture * 0.05;
            return slItem;
        }

        private StudyLoadItem ConsultationEveningLearningRule(AllocationItem allocItem, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.ControlWorkForEveningLearning);
            slItem.Hours = (double)allocItem.HoursLecture * 0.01;
            return slItem;
        }

        private StudyLoadItem ConsultationDistanceLearnibgRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.ControlWorkForDistanceLearning);

            var studentsCount = CountStudents(allocItem, allocation);
            var individualHours = (double)studentsCount * 0.5; //?
            var commonHours = (double)allocItem.HoursLecture * 0.01;

            slItem.Hours = individualHours + commonHours;
            return slItem;
        }

        private StudyLoadItem ConsultationForExamRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.ConsultationForExam);
            slItem.Hours = (from studyGroup in allocItem.StudyGroups
                            select
                                 (from state in studyGroup.StudyGroupStates
                                  where IsCurrentYearGroup(state, studyGroup, allocItem, allocation)
                                  select state.CountStudent < 6 ? 1 : 2).Sum()
                           ).Sum();
            return slItem;
        }
        #endregion консультации

        #region контроль
        private StudyLoadItem TestRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.Test);
            var multiplier = allocItem.Discipline.Title.Equals("инженерная графика") ? 0.4 : 0.2;

            slItem.Hours = CountStudents(allocItem, allocation) * multiplier;

            return slItem;
        }

        private StudyLoadItem ExamRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.Exam);

            slItem.Hours = CountStudents(allocItem, allocation) * 0.3;
            if (allocItem.Discipline.Title == "начертательная геометрия")
                slItem.Hours += CountGroup(allocItem, allocation) * 4;

            return slItem;
        }

        private StudyLoadItem ExamGOSRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.ExamGOS);

            slItem.Hours = CountStudents(allocItem, allocation) * 0.5 * 3.5;

            return slItem;
        }

        private StudyLoadItem SRSForFullTimeLearningRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.SRSForFullTimeLearning);

            double c = allocItem.HoursSRS > 110 ? 110 : allocItem.HoursSRS;
            double k = CountStudents(allocItem, allocation);

            switch (allocItem.ETypeSRS)
            {
                case ETypeSRS.Semesterwork:
                    if (allocItem.Discipline.Title.Equals("иностранный язык"))
                        k *= 0.18;
                    else if (allocItem.Discipline.Title == "инженерная графика"
                             || allocItem.Discipline.Title == "начертательная геометрия")
                        k *= 1.2;
                    else
                        k *= 0.7;
                    break;
                case ETypeSRS.Coursework:
                    k *= 3.0;
                    break;
                case ETypeSRS.Project:
                    k *= 0.4;
                    break;
                default:
                    throw new Exception("Нет подходящего выбора для правила SRSForFullTimeLearningRule");
            }
            slItem.Hours = 0.01 * c + k * (40 + 0.6 * c) / (180 - c);

            return slItem;
        }

        private StudyLoadItem RaitingRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.Raiting);
            var commonHours = allocItem.HoursLab + allocItem.HoursLecture + allocItem.HoursPractice + allocItem.HoursSRS;
            slItem.Hours = CountStudents(allocItem, allocation) * commonHours;
            return slItem;
        }

        private StudyLoadItem ControlWorkForEveningLearningRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.ControlWorkForEveningLearning);

            slItem.Hours = CountStudents(allocItem, allocation);

            switch (allocItem.Discipline.Title)
            {
                case "высшая математика":
                case "сопротивление материалов":
                    slItem.Hours *= 0.9;
                    break;
                case "теоретическая механика":
                case "теплотехника":
                case "гидравлика":
                case "начертательная геометрия":
                case "инженерная графика":
                    slItem.Hours *= 0.7;
                    break;
                default:
                    slItem.Hours *= 0.5;
                    break;
            }

            return slItem;
        }

        private StudyLoadItem ControlWorkForDistanceLearningRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.ControlWorkForDistanceLearning);

            slItem.Hours = CountStudents(allocItem, allocation) * 0.75;

            return slItem;
        }

        private StudyLoadItem CourseWorkForDistanceLearningRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.CourseWorkForDistanceLearning);
            slItem.Hours = CountStudents(allocItem, allocation) * 1.5;
            return slItem;
        }

        private StudyLoadItem CourseProjectForDistanceLearning(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.CourseWorkForDistanceLearning);
            slItem.Hours = CountStudents(allocItem, allocation) * 3;
            return slItem;
        }
        #endregion контроль

        #region дипломы и дисертации
        // Руководство квалификационной работой бакалавра
        private StudyLoadItem BachelorWorkRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.BachelorWork);
            slItem.Hours = CountStudentsInSemesterRange(allocItem, allocation, 7, 8) * 15;

            return slItem;
        }
        // Участие в ГАК на защите работ бакалавров
        private StudyLoadItem GAKBachelorWorkRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.BachelorWork);
            slItem.Hours = 15 * CountStudentsInSemesterRange(allocItem, allocation, 8, 8);
            return slItem;
        }
        // Руководство НИР магистратуры
        private StudyLoadItem NIRBachelorWorkRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.NIRBachelorWork);
            slItem.Hours = CountStudentsInSemesterRange(allocItem, allocation, 9, 12) * 18;
            return slItem;
        }
        // Руководство дипломных работ и проектов специалитов и участие в ГЭК
        private StudyLoadItem SpecialistWorkRule(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.SpecialistWork);
            slItem.Hours = CountStudentsInSemesterRange(allocItem, allocation, 9, 11) * 33;
            return slItem;
        }
        // Руководство магистерской диссертацией и участие в работе ГЭК
        private StudyLoadItem MasterWorkRule(AllocationItem allocItem, StudyLoad studyLoad)
        {
            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.MasterWork);
            //?
            return slItem;
        }
        #endregion дипломы и дисертации

        #region практика
        //?
        private StudyLoadItem Practice(AllocationItem allocItem, Allocation allocation, StudyLoad studyLoad)
        {

            StudyLoadItem slItem = InitItem(allocItem, studyLoad, ETypeLoadItem.Practice);
            slItem.Hours = CountStudents(allocItem, allocation) * 0.6 * 18;
            return slItem;
        }
        #endregion практика
        #endregion набор правил
    }
}
